package top.wshape1.rpc.loadbalance;

import lombok.extern.slf4j.Slf4j;
import top.wshape1.rpc.common.entity.ServiceMeta;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2024-02-22
 */

@Slf4j
public class LeastActiveLoadBalance extends AbstractLoadBalance {

    /**
     * 服务调用次数
     * ServiceName -> {Address -> Count}
     */
    private final ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>> SERVICE_ACTIVE = new ConcurrentHashMap<>();

    @Override
    protected ServiceMeta doSelect(List<ServiceMeta> serviceMetas) {
        log.debug("================================================================================={}", SERVICE_ACTIVE);
        String serviceName = serviceMetas.get(0).getServiceName();

        ConcurrentHashMap<String, Integer> map = SERVICE_ACTIVE.get(serviceName);
        if (map == null) {
            SERVICE_ACTIVE.putIfAbsent(serviceName, new ConcurrentHashMap<>());
            map = SERVICE_ACTIVE.get(serviceName);
        }
        // 最少调用次数
        int leastActive = Integer.MAX_VALUE;
        // 相同的有多少个
        int leastCount = 0;
        // 它们的索引
        int[] leastIndexes = new int[serviceMetas.size()];

        for (int i = 0; i < serviceMetas.size(); i++) {
            ServiceMeta serviceMeta = serviceMetas.get(i);
            String address = serviceMeta.getAddress();

            // 获取调用次数
            Integer active = map.get(address);
            active = (active == null) ? 0 : active;

            if (active < leastActive) {
                leastActive = active;
                leastCount = 1;
                leastIndexes[0] = i;
            } else if (active == leastActive) {
                leastIndexes[leastCount++] = i;
            }
        }

        // 如果只有一个最小
        if (leastCount == 1) {
            ServiceMeta serviceMeta = serviceMetas.get(leastIndexes[0]);
            // 调用次数加一
            map.compute(serviceMeta.getAddress(), (k, v) -> {
                if (v == null)
                    return 1;
                return v + 1;
            });
            return serviceMeta;
        }

        // 如果有多个最小，随机一个
        if (leastCount > 1) {
            ServiceMeta serviceMeta = serviceMetas.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
            // 调用次数加一
            map.compute(serviceMeta.getAddress(), (k, v) -> {
                if (v == null)
                    return 1;
                return v + 1;
            });
            return serviceMeta;
        }

        return null;
    }
}
